/*
 * This file is part of algowala.
 *
 * algowala is copyright (c) 2012 Jagannathan Sampath <algowala@jagernot.com>
 *
 * algowala is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * algowala is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with algowala.  If not, see <http://www.gnu.org/licenses/>.
 *
*/

#ifndef __curve_editor
#define __curve_editor

#include <vector>
#include <map>
#include <list>
#include <string>

#include "curve.h"
#include "multi_curve.h"
#include "curve_listener.h"
#include "basic_viewer.h"
#include "ui.h"
#include "box_selector.h"

class curve_library;

struct hit_t {

  multi_curve* crv; // curve hit
  int crv_id; // curve index
  enum {NONE = 0, VERTEX, LEFT_TANGENT, RIGHT_TANGENT}; // things that can be hit in curve
  unsigned int what; // what was hit in curve
  int id; // id of that which was hit

  // tangent vectors from corresponding vertex
  //
  point<double> left_tangent, right_tangent;
  double left_tangent_magnitude, right_tangent_magnitude;

  hit_t (multi_curve* cv = 0, int cid = -1, unsigned int w = NONE, int i = -1);
  void clear ();
  int operator()();
  bool operator== (const hit_t& h) {return ((crv == h.crv) && (what == h.what) && (id == h.id));}

};

struct undo_t {
  int i;
  multi_curve curve;
  window win;
  undo_t (int ii, const multi_curve& mc, window& w) : i(ii), curve (mc), win (w) {}
};

typedef undo_t redo_t;

struct curve_info {

  multi_curve* curve;
  bool mark_segments;
  bool disabled;

  curve_listener* lisner;

  curve_info  (multi_curve* c, curve_listener* l, bool disabled = false);
  curve_info ();

};

struct curve_editor : basic_viewer {

  std::vector <curve_info> curveinfo; // edited curves
  int curcrv;

  // curve editor features
  //

  bool carry_tangents; // when vertex moves, their tangents move too.
  bool mirror_tangents; // when 1 tangent of a vertex moves, the other moves too.
    
  //
  // hit testing
  //

  box_selector<double> selector;

  std::vector <hit_t> hitlist;
  int hlid;

  hit_t pik;
  bool use_existing_hit;
  void hittest (multi_curve* crv, int crv_id, const points_array& points, unsigned int what);
  hit_t hittest ();
  void clear_hit (hit_t& h);
  void calc_hit_params (hit_t& h);


  // vertex/tangent operations
  enum {DO_NOTHING = 0, MOVE_PICKED, MOVE_ALL, SCALE_ALL};
  int todo;
  hit_t mov, ins, del, mir, rep;
  bool move ();
  void insert ();
  void remove ();
  bool move (int);
  bool move (hit_t& hit, double x, double y);
  void replace ();
  void mirror (int whole_curve = 0);
  void resolution (int i, int dir);

  // scratch curve
  bool show_scratch_curve;
  points_array win_scratch_points, curv_scratch_points;
  multi_curve scratch_curve;

  // undo, redo
  //

  std::list <undo_t> undos;
  std::list <redo_t> redos;
  void dodo (std::list<undo_t>& do1, std::list<undo_t>& do2, std::string mesg);

  // copy & paste
  static multi_curve copy;
  void paste (hit_t& h);

  // curve library
  //
  curve_library* library;
  void add_curve ();
  void replace_curve ();
  void insert_curve ();
  void load_curve (int dir);

  // settings
  //
  curve_editor (const std::string& settingsf);
  ~curve_editor ();
  void load (const std::string& fname);
  void save ();

  // ui
  bool handle_input ();

  // curve mgmt ops
  void add (multi_curve* crv, curve_listener* lsnr);
  int num_curves ();
  void clear ();

  curve_info& get_curve_info (int i);
  multi_curve* get_curve (int i);

  // curve selection ops
  void pick (int k);
  void toggle (int k);
  void enable (int k);
  void enable_all ();
  bool one_curve_enabled ();
  std::string selection ();

  void draw_common ();
  void draw_all ();
  void draw ();

  void clear_scratch_curve ();
  void draw_scratch_curve ();

  void draw_curve (multi_curve* crv);
  void draw_tangents (multi_curve* crv);
  void draw_vertices (multi_curve* crv);
  void draw_handle (const point<double>& p);
  void draw_tangent (const point<double>& p, const point<double>& t);

  void draw_curves ();
  void mark_curve_segments ();
  void draw_vertices ();
  void draw_tangents ();

  void attach_library (curve_library* lib);

  void enter ();
  void bg ();

};
#endif
